package grape.common.service.common;

import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import grape.common.exception.CBaseException;
import grape.common.exception.ExceptionTools;
import grape.common.exception.runtime.InvalidParamsException;
import grape.common.service.common.dataconstraint.ConstraintCompiledContent;
import grape.common.service.po.IDBasePo;
import grape.common.service.po.TreeBasePo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static grape.common.service.po.TreeBasePo.INIT_LEVEL;
import static grape.common.service.po.TreeBasePo.maxLevel;

/**
 * 树操作
 * Created by yangwei
 * Created at 2019/9/23 11:03
 */
public interface IBaseTreeService<Po extends TreeBasePo<Po>> extends IBaseService<Po> {


    /**
     * 查询第一级
     * @return
     */
    default List<Po> getRoot(){
        return list(Wrappers.<Po>query().eq(TreeBasePo.COLUMN_LEVEL,INIT_LEVEL).isNull(TreeBasePo.COLUMN_PARENT_ID));
    }
    /**
     * 查询第一级
     * @return
     */
    default List<Po> getRoot(Po query){
        return list(Wrappers.<Po>query(query).eq(TreeBasePo.COLUMN_LEVEL,INIT_LEVEL).isNull(TreeBasePo.COLUMN_PARENT_ID));
    }
    /**
     * 查询第一级，带数据约束
     * @return
     */
    default List<Po> getRoot(List<ConstraintCompiledContent> constraintContents){
        return list(Wrappers.<Po>query()
                .eq(TreeBasePo.COLUMN_LEVEL,INIT_LEVEL)
                .isNull(TreeBasePo.COLUMN_PARENT_ID)
                .and(wq->
                        {
                            //wq.apply(sqlSegment);
                            for (ConstraintCompiledContent constraintContent : constraintContents) {
                                wq.or(wqq -> wqq.apply(constraintContent.getCompiledSqlContent()));
                            }
                            return wq;
                        }
                ));

    }
    /**
     * 查询第一级并且等于该id
     * @return
     */
    default List<Po> getRoot(String id){
        assertParamNotEmpty(id,"id不能为空");
        return list(Wrappers.<Po>query().eq(TreeBasePo.COLUMN_LEVEL,INIT_LEVEL).isNull(TreeBasePo.COLUMN_PARENT_ID).eq(TreeBasePo.COLUMN_ID,id));

    }
    /**
     * 查询子一级节点
     * @param parentId
     * @return
     */
    default List<Po> getChildren(String parentId){
        assertParamNotEmpty(parentId,"parentId不能为空");
        return list(Wrappers.<Po>query().eq(TreeBasePo.COLUMN_PARENT_ID,parentId));
    }
    /**
     * 查询子一级节点
     * @param parentId
     * @param query
     * @return
     */
    default List<Po> getChildren(String parentId,Po query){
        assertParamNotEmpty(parentId,"parentId不能为空");
        if(query == null){
            return list(Wrappers.<Po>query().eq(TreeBasePo.COLUMN_PARENT_ID,parentId));
        }else {
            return list(Wrappers.<Po>query(query).eq(TreeBasePo.COLUMN_PARENT_ID,parentId));
        }
    }
    /**
     * 查询子一级节点,带数据约束
     * @param parentId
     * @return
     */
    default List<Po> getChildren(String parentId,List<ConstraintCompiledContent> constraintContents){
        assertParamNotEmpty(parentId,"parentId不能为空");
        return list(Wrappers.<Po>query().eq(TreeBasePo.COLUMN_PARENT_ID,parentId).and(wq->
                {
                    //wq.apply(sqlSegment);
                    for (ConstraintCompiledContent constraintContent : constraintContents) {
                        wq.or(wqq -> wqq.apply(constraintContent.getCompiledSqlContent()));
                    }
                    return wq;
                }
        ));
    }
    /**
     * 查询子一级节点，结果必须是等于限制id或其子节点
     * @param parentId
     * @param limitId 限制id
     * @return
     */
    default List<Po> getChildren(String parentId,String limitId){
        assertParamNotEmpty(parentId,"parentId不能为空");
        Po po = getById(limitId);
        return list(Wrappers.<Po>query().eq(TreeBasePo.COLUMN_PARENT_ID,parentId)
                .and((wq)->wq.eq(IDBasePo.COLUMN_ID,limitId).or().eq(TreeBasePo.COLUMN_PARENT_ID + po.getLevel(),limitId))
        );
    }
    /**
     * 查询子一级节点,是否有子节点
     * @param parentId
     * @return
     */
    default boolean hasChildren(String parentId){
        int count = getChildrenCount(parentId);
        return count > 0;
    }

    /**
     * 获取所有的子孙节点
     * @param parentId
     * @return
     */
    default List<Po> getAllChildren(String parentId){
        if (parentId == null) {
            return null;
        }
        Po parent = getById(parentId);
        if (parent == null) {
            return null;
        }
        Map<String,Object> p = new HashMap<>(1);
        p.put(TreeBasePo.COLUMN_PARENT_ID + parent.getLevel(),parentId);
        return (List<Po>) listByMap(p);
    }

    /**
     * 获取子一级节点的数目
     * @param parentId
     * @return
     */
    default int getChildrenCount(String parentId){
        if (parentId == null) {
            throw new InvalidParamsException("parentId不能为空");
        }
        Map<String,Object> p = new HashMap<>(1);
        p.put(TreeBasePo.COLUMN_PARENT_ID,parentId);
        return count(Wrappers.<Po>query().eq(TreeBasePo.COLUMN_PARENT_ID,parentId));
    }
    /**
     * 查询父级
     * @param id
     * @return
     */
    default Po getParent(String id){
        if (id == null) {
            return null;
        }
        Po po = getById(id);
        if (po != null) {
            return getById(po.getParentId());
        }
        return null;
    }

    /**
     * 获取所有父级
     * @param id
     * @return
     */
    default List<Po> getAllParents(String id){
        if (id == null) {
            return null;
        }
        Po dbPo = getParent(id);
        if (dbPo == null) {
            return null;
        }
        List<String> parentIds = new ArrayList<>(maxLevel - 1);
        if (dbPo.getParentId1() != null) {
            parentIds.add(dbPo.getParentId1());
        }
        if (dbPo.getParentId2() != null) {
            parentIds.add(dbPo.getParentId2());
        }
        if (dbPo.getParentId3() != null) {
            parentIds.add(dbPo.getParentId3());
        }
        if (dbPo.getParentId4() != null) {
            parentIds.add(dbPo.getParentId4());
        }
        if (dbPo.getParentId5() != null) {
            parentIds.add(dbPo.getParentId5());
        }
        if (dbPo.getParentId6() != null) {
            parentIds.add(dbPo.getParentId6());
        }
        if (dbPo.getParentId7() != null) {
            parentIds.add(dbPo.getParentId7());
        }
        if (dbPo.getParentId8() != null) {
            parentIds.add(dbPo.getParentId8());
        }
        if (dbPo.getParentId9() != null) {
            parentIds.add(dbPo.getParentId9());
        }
        if (dbPo.getParentId10() != null) {
            parentIds.add(dbPo.getParentId10());
        }
        return (List<Po>) listByIds(parentIds);
    }

    /**
     * 检查树结构是否完整，该检查一旦检查到异常数据就停止，而不会汇总出所有数据的异常，因为父级有异常子级就不保证正确了，所以先处理了再检查
     * @param parent 对parent本身没有做检查，启用必须传null对全量做检查，如果不传null请确保parent本身的正确
     * @throws CBaseException 返回不完整的异常
     */
    default void checkTreeStruct(Po parent) throws CBaseException {

        int level = parent == null? INIT_LEVEL:parent.getLevel() + 1;
        Map<String, String> parentIdx = new HashMap<>(10);
        for (int i = 1; i < maxLevel; i++) {
            String fieldName = TreeBasePo.PROPERTY_PARENT_ID + i;
            Object fieldValueObj = ReflectUtil.getFieldValue(parent, fieldName);
            String fieldValue = fieldValueObj == null ? null : fieldValueObj.toString();
            parentIdx.put(fieldName, parent == null ? null : fieldValue);
        }
        if (parent != null) {
            String fieldName = TreeBasePo.PROPERTY_PARENT_ID + parent.getLevel();
            parentIdx.put(fieldName, parent.getId());
        }
        List<Po> children = null;
        if (parent == null) {
            children = getRoot();
        }else {
            children = getChildren(parent.getId());
        }
        if (!isEmpty(children)) {
            for (Po po : children) {
                if(po.getLevel() != level){
                    throw new CBaseException("id=" + po.getId() + " level应该为"+ level +"实际为" + po.getLevel());
                }
                for (int i = 1; i < maxLevel; i++) {
                    String fieldName = TreeBasePo.PROPERTY_PARENT_ID + i;
                    Object fieldValueObj = ReflectUtil.getFieldValue(po, fieldName);
                    String fieldValue = fieldValueObj == null ? null : fieldValueObj.toString();
                    if( !isEqual(parentIdx.get(fieldName),fieldValue)){
                        throw new CBaseException("id=" + po.getId() + " "+ (fieldName) +"应该为"+ parentIdx.get(fieldName) +"实际为" + fieldValue);
                    }
                }
                checkTreeStruct(po);
            }
        }

    }
    /**
     * 根据id和父id查询
     * @param id
     * @param parentId
     * @return
     */
    default Po getByIdAndParentId(String id,String parentId){
        if (isStrAnyEmpty(id,parentId)) {
            throw new InvalidParamsException("id 和 parentId" + "都不能为空");
        }
        return getOne(Wrappers.<Po>query().eq(TreeBasePo.COLUMN_PARENT_ID, parentId).eq(TreeBasePo.COLUMN_ID, id));
    }

    /**
     * 根据风节点，设置子节点parentIdx的值包括parentId本身
     * @param child
     * @param parentId
     * @return
     */
    default Po initParentIdXByParent(Po child,String parentId){
        Po parent = getById(parentId);
        return initParentIdXByParent(child, parent);
    }

    /**
     * 根据父节点，设置子节点parentIdx的值包括parentId本身
     * @param child
     * @param parent
     * @return
     */
    default Po initParentIdXByParent(Po child,Po parent){
        // 先初始化child
        child.setLevel(INIT_LEVEL);
        child.setParentId(null);
        String parentIdx = null;
        for (int i = 1; i < maxLevel; i++) {
            parentIdx = TreeBasePo.PROPERTY_PARENT_ID + i;
            ReflectUtil.setFieldValue(child,parentIdx,null);
        }

        if (parent != null) {
            child.setLevel(parent.getLevel() + 1);
            if(child.getLevel() > maxLevel){
                throw ExceptionTools.newRE("最大支持的树深度为" + maxLevel);
            }
            child.setParentId(parent.getId());
            for (int i = 1; i < maxLevel; i++) {
                parentIdx = TreeBasePo.PROPERTY_PARENT_ID + i;
                if (child.getLevel().equals(i + 1)) {
                    ReflectUtil.setFieldValue(child,parentIdx,parent.getId());
                    break;
                }else {
                    ReflectUtil.setFieldValue(child,parentIdx,ReflectUtil.getFieldValue(parent,parentIdx));
                }
            }
        }
        return child;
    }
    /**
     * 根据父级的level设置查询的parentIdx
     * @param child
     * @param parentId
     */
    default Po initQueryChildrenParentIdXByParentLevel(Po child,String parentId){
        Po parent = getById(parentId);
        return initQueryChildrenParentIdXByParentLevel(child, parent);
    }
    /**
     * 根据父级的level设置查询的parentIdx
     * @param child
     * @param parent
     */
    default Po initQueryChildrenParentIdXByParentLevel(Po child,Po parent){
        String parentIdx = TreeBasePo.PROPERTY_PARENT_ID + parent.getLevel();
        ReflectUtil.setFieldValue(child,parentIdx,parent.getId());
        return child;
    }
    /**
     * 添加子节点
     * @param entity
     * @param parentId
     * @return
     */
    default Po insertChild(Po entity, String parentId){
        entity = initParentIdXByParent(entity, parentId);

        return insert(entity);
    }

    /**
     * 将id节点移动到parentId下，包括所有子节点
     * @param id
     * @param parentId
     * @return  返回涉及到的数据条数
     */
    default int moveNode(String id,String parentId){

        int result = 0;
        assertParamNotEmpty(id,"移动的节点id不能为空");
        Po entity = getById(id);
        assertParamNotNull(entity,"移动的节点不存在");
        if (isEqual(id,parentId)) {
            throw new InvalidParamsException("不能将节点本身做为父节点");
        }
        entity = initParentIdXByParent(entity, parentId);
        // 更新开始 主要是把parentIdX可以更新为null
        UpdateWrapper<Po> update = Wrappers.update();
        update.set(TreeBasePo.COLUMN_PARENT_ID,entity.getParentId());
        update.eq(IDBasePo.COLUMN_ID, entity.getId());
        String propertyParentIdx = null;
        String columnParentIdx = null;
        for (int i = 1; i < maxLevel; i++) {
            columnParentIdx = TreeBasePo.COLUMN_PARENT_ID + i;
            propertyParentIdx = TreeBasePo.PROPERTY_PARENT_ID + i;
            update.set(columnParentIdx,ReflectUtil.getFieldValue(entity,propertyParentIdx));
        }
        boolean entityUpdateResult = update(entity,update);
        // 更新结束
        if(entityUpdateResult){
            result++;
        }
        List<Po> children = getChildren(id);
        if (!isEmpty(children)) {
            for (Po child : children) {
                result += moveNode(child.getId(), id);
            }
        }
        return result;
    }
}
